﻿using System;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

namespace Infrastructure.Security
{
    /// <summary>
    /// AES加解密
    /// </summary>
    public static class Aes
    {
        private const string ConfigKey = "{&f-K)4,q<%!r*|+";
        private const string DataKey = "?|w`NL[;v*M.@:Y$";

        /// <summary>
        /// 使用AES算法对字符串进行加密
        /// </summary>
        /// <param name="plainText">明文</param>
        /// <param name="key">密钥</param>
        /// <returns>密文</returns>
        public static string Encrypt(string plainText, string key)
        {
            // 密钥为空
            if (string.IsNullOrEmpty(key))
            {
                throw new ArgumentException("Invalid key");
            }

            if (plainText == null)
            {
                plainText = string.Empty;
            }

            string cipherText = string.Empty;

            using (var aesAlg = System.Security.Cryptography.Aes.Create())
            {
                // 设置填充模式
                aesAlg.Padding = PaddingMode.ISO10126;

                // 加载密钥
                aesAlg.Key = Encoding.UTF8.GetBytes(key);

                // 创建AES加密对象
                ICryptoTransform encryptor = aesAlg.CreateEncryptor();

                // 进行AES加密
                Byte[] toEncrypt = UTF8Encoding.UTF8.GetBytes(plainText);
                Byte[] encrypted = encryptor.TransformFinalBlock(toEncrypt, 0, toEncrypt.Length);

                // 获得初始化向量
                Byte[] iv = aesAlg.IV;

                // 将初始化向量数组和密文数组进行合并
                Byte[] result = new Byte[encrypted.Length + iv.Length];

                iv.CopyTo(result, 0);
                encrypted.CopyTo(result, iv.Length);

                // 密文由初始化向量+密文合并而成
                cipherText = Convert.ToBase64String(result, 0, result.Length);
            }

            return cipherText;
        }

        /// <summary>
        /// 使用AES算法对字符串进行加密
        /// </summary>
        /// <param name="plainText">明文</param>
        /// <param name="key">密钥</param>
        /// <returns>Url合法的密文</returns>
        public static string EncryptUrl(string plainText, string key)
        {
            // 加密并进行Url编码
            string cipherText = Encrypt(plainText, key);
            cipherText = Url.Encode(cipherText);

            return cipherText;
        }

        /// <summary>
        /// 使用默认密钥对对字符串进行AES加密。
        /// </summary>
        /// <param name="plainText">明文</param>
        /// <param name="keyType">密钥类型</param>
        /// <returns>Url合法的密文</returns>
        public static string EncryptUrl(string plainText, KeyType keyType)
        {
            string key = string.Empty;

            // 根据密钥类型加载默认密钥
            switch (keyType)
            {
                case KeyType.Config:
                    key = ConfigKey;
                    break;
                case KeyType.Data:
                    key = DataKey;
                    break;
                default:
                    key = ConfigKey;
                    break;
            }

            // 加密并进行Url编码
            string cipherText = Encrypt(plainText, key);
            cipherText = Url.Encode(cipherText);

            return cipherText;
        }

        /// <summary>
        /// 使用AES算法对字符串进行解密
        /// </summary>
        /// <param name="cipherText">密文</param>
        /// <param name="key">密钥</param>
        /// <returns>明文</returns>
        public static string Decrypt(string cipherText, string key)
        {
            // 密钥为空
            if (string.IsNullOrEmpty(key))
            {
                throw new ArgumentException("Invalid key");
            }

            if (cipherText == null)
            {
                cipherText = string.Empty;
            }

            string plainText = string.Empty;

            using (var aesAlg = System.Security.Cryptography.Aes.Create())
            {
                // 设置填充模式
                aesAlg.Padding = PaddingMode.ISO10126;

                // 加载密钥
                aesAlg.Key = Encoding.UTF8.GetBytes(key); ;

                // 从密文分离初始化向量数组和密文数组
                Byte[] iv = Convert.FromBase64String(cipherText).Take(16).ToArray();
                Byte[] toDecrypt = Convert.FromBase64String(cipherText).Skip(16)
                    .Take(Convert.FromBase64String(cipherText).Length - 16).ToArray();

                // 加载初始化向量
                aesAlg.IV = iv;

                // 创建AES解密对象
                ICryptoTransform decryptor = aesAlg.CreateDecryptor();

                // 进行AES解密
                Byte[] decrypted = decryptor.TransformFinalBlock(toDecrypt, 0, toDecrypt.Length);

                // 明文
                plainText = System.Text.UTF8Encoding.UTF8.GetString(decrypted);
            }

            return plainText;
        }

        /// <summary>
        /// 使用AES算法对字符串进行解密
        /// </summary>
        /// <param name="cipherText">Url合法的密文</param>
        /// <param name="key">密钥</param>
        /// <returns>明文</returns>
        public static string DecryptUrl(string cipherText, string key)
        {
            if (cipherText == null)
            {
                cipherText = string.Empty;
            }

            // 还原密文
            cipherText = Url.Decode(cipherText);

            // 解密
            string plainText = Decrypt(cipherText, key);

            return plainText;
        }

        /// <summary>
        /// 使用默认密钥对对字符串进行AES解密。
        /// </summary>
        /// <param name="cipherText">Url合法的密文</param>
        /// <param name="keyType">密钥类型</param>
        /// <returns>明文</returns>
        public static string DecryptUrl(string cipherText, KeyType keyType)
        {
            if (cipherText == null)
            {
                cipherText = string.Empty;
            }

            // 还原密文
            cipherText = Url.Decode(cipherText);

            string key = string.Empty;

            // 根据密钥类型加载默认密钥
            switch (keyType)
            {
                case KeyType.Config:
                    key = ConfigKey;
                    break;
                case KeyType.Data:
                    key = DataKey;
                    break;
                default:
                    key = ConfigKey;
                    break;
            }

            // 解密
            string plainText = Decrypt(cipherText, key);

            return plainText;
        }

        /// <summary>
        /// 描述默认密钥类型
        /// </summary>
        public enum KeyType
        {
            /// <summary>
            /// 配置文件的默认密钥
            /// </summary>
            Config,
            /// <summary>
            /// 数据的默认密钥
            /// </summary>
            Data
        }
    }
}
